require 'hmac-sha1'
require 'digest/md5'
require 'base64'

module API::V2
  class AuthTokens < Grape::API
    helpers do
      def handle_auth_response client_type, client_identifier, error
        if error
          render_api_error!(error, 401)
        else
          auth_token = AuthToken.create(client_identifier: client_identifier, client_type: client_type)
          present :client_id, auth_token.id
          present :auth_token, auth_token.token
        end
      end

      # generate secure identify algorithm
      def generate_secure_identify mac, timestamp
        # strip `:` in mac address and then reverse it
        mac.delete!(':').reverse!
        # transfer the first and last half of it
        mac = mac[6..11] + mac[0..5]
        # generate a number array based on timestamp to perform sort
        sort_weight = Digest::MD5.hexdigest(timestamp.to_s)[0..23].chars.each_slice(2).map { |s| s.join.to_i 16 }
        # then sort it
        index = -1
        mac = mac.chars.to_a.sort_by! { |k| sort_weight[index += 1] }.join
        # hmac-sha1
        Base64.urlsafe_encode64(HMAC::SHA1.digest(private_key, mac)).strip
      end

      # generate private key
      def private_key
        key_file = Rails.root.join('.private_key')
        if File.exist?(key_file)
          # Use the existing token.
          File.read(key_file).chomp
        else
          # Generate a new token and store it in key_file.
          token = SecureRandom.hex(64)
          File.write(key_file, token)
          token
        end
      end
    end

    resource :auth_tokens do
      params do
        requires :mac, type: String, regexp: /\A([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}\z/
        requires :timestamp, type: Integer
        requires :result, type: String
      end
      post :terminal do
        pass = generate_secure_identify(params[:mac], params[:timestamp]) == params[:result]
        error = !pass && { message: "the result doesn't match", error_code: 1401 }
        handle_auth_response 'terminal', params[:mac], error
      end

      params do
        requires :mobile_number, type: String, regexp: /\A\d{11}\z/
        requires :verify_code, type: String, regexp: /\A\d+\z/
      end 
      post :app do
        auth_message = AuthMessage.find_by_mobile_number(params[:mobile_number])
        if auth_message && auth_message.activated_at < Time.now
          error = { message: "verify code is out-of-date", error_code: 1401 }
        else
          pass = auth_message && (auth_message.verify_code == params[:verify_code])
          error = !pass && { message: "verify code doesn't match", error_code: 2401 }
        end

        handle_auth_response 'app', params[:mobile_number], error
      end

    end
  end
end